/**HEADER********************************************************************
* 
* Copyright (c) 2008-2009 Freescale Semiconductor;
* All Rights Reserved
*
* Copyright (c) 2004-2008 Embedded Access Inc.;
* All Rights Reserved
*
* Copyright (c) 1989-2008 ARC International;
* All Rights Reserved
*
*************************************************************************** 
*
* THIS SOFTWARE IS PROVIDED BY FREESCALE "AS IS" AND ANY EXPRESSED OR 
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  
* IN NO EVENT SHALL FREESCALE OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 
* THE POSSIBILITY OF SUCH DAMAGE.
*
**************************************************************************
*
* $FileName: serl_pol_st16550.c$
* $Version : 3.5.1.0$
* $Date    : Feb-24-2010$
*
* Comments:
*
*   This file contains the functions for the polled serial I/O
*   driver for the 16550 compatible UART.
*
*END*********************************************************************/

#include "mqx.h"
#include "bsp.h"
#include "io_prv.h"
#include "charq.h"
#include "fio_prv.h"
#include "serplprv.h"

extern _mqx_uint _st16550_serial_polled_init(ST16550_SERIAL_INIT_STRUCT_PTR, pointer _PTR_, char_ptr);
extern _mqx_uint _st16550_serial_polled_deinit(ST16550_SERIAL_INIT_STRUCT_PTR, ST16550_SERIAL_INFO_STRUCT_PTR );
extern char      _st16550_serial_getc(ST16550_SERIAL_INFO_STRUCT_PTR);
extern void      _st16550_serial_putc(ST16550_SERIAL_INFO_STRUCT_PTR, char);
extern boolean   _st16550_serial_status(ST16550_SERIAL_INFO_STRUCT_PTR);
extern _mqx_uint _st16550_serial_ioctl(ST16550_SERIAL_INFO_STRUCT_PTR, _mqx_uint, pointer);

/*FUNCTION*-------------------------------------------------------------------
* 
* Function Name    : _st16550_uart_serial_polled_install
* Returned Value   : _mqx_uint a task error code or MQX_OK
* Comments         :
*    Install a polled uart serial device.
*
*END*----------------------------------------------------------------------*/

_mqx_uint _st16550_serial_polled_install
   (
      /* [IN] A string that identifies the device for fopen */
      char_ptr           identifier,
  
      /* [IN] The I/O init data pointer */
      pointer            init_data_ptr,
      
      /* [IN] The I/O queue size to use */
      _mqx_uint          queue_size
   )
{ /* Body */

/* Start CR 1026 */
   return _io_serial_polled_install(identifier,
      (_mqx_uint (_CODE_PTR_)(pointer, pointer _PTR_, char _PTR_))
         _st16550_serial_polled_init,
      (_mqx_uint (_CODE_PTR_)(pointer, pointer))
         _st16550_serial_polled_deinit,
      (char (_CODE_PTR_)(pointer))
         _st16550_serial_getc,
      (void (_CODE_PTR_)(pointer, char))
         _st16550_serial_putc,
      (boolean (_CODE_PTR_)(pointer))
         _st16550_serial_status,
      (_mqx_uint (_CODE_PTR_)(pointer, _mqx_uint, pointer))
         _st16550_serial_ioctl,
      init_data_ptr, queue_size);
/* End CR 1026 */

} /* Endbody */


/*FUNCTION****************************************************************
* 
* Function Name    : reconfigure
* Returned Value   : MQX_OK or an MQX error code.
* Comments         :
*    This function re-initializes the uart given changes made to the
* ST16550_SERIAL_INFO_STRUCT.  It was originally written to implement
* the ioctl command IO_IOCTL_SERIAL_SET_DATA_BITS.
*
*END*********************************************************************/

static uint_32 reconfigure
   (
      ST16550_SERIAL_INFO_STRUCT_PTR  io_info_ptr
   )
{ /* Body */
   volatile ST16550_UART_STRUCT _PTR_      uart_ptr = io_info_ptr->UART_PTR;
   volatile ST16550_SERIAL_INIT_STRUCT_PTR io_init_ptr = &io_info_ptr->INIT;
   volatile uint_32                        tmp;
   uint_32                                 timeout;
   uint_32                                 div;

   uart_ptr->MCR = 0; /* tell chip to NOT interrupt */   
   _BSP_IO_EIEIO;
   uart_ptr->IER = 0; /* Reset interrupts */
   _BSP_IO_EIEIO;
   uart_ptr->IIR = 0; /* Reset FIFOs to char mode */
   _BSP_IO_EIEIO;

   tmp  = uart_ptr->LSR;

   /* Check to see if FIFO is present (it is the IIR register) */
   uart_ptr->IIR = 0xcf;  /* Fifo mode 1, reset, trigger=14 */
   _BSP_IO_EIEIO;
   tmp = uart_ptr->IIR;
   _BSP_IO_EIEIO;
   if ((tmp & 0xc0) == 0xc0) {
      /* Fifos are present */
      io_info_ptr->FIFO_CONTROL_REG = ST16550_UART_IIR_FIFO_ENABLE     | 
                                      ST16550_UART_IIR_RCVR_FIFO_RESET | 
                                      ST16550_UART_IIR_XMIT_FIFO_RESET |
                                      ST16550_UART_IIR_DMA_MODE_SELECT |
                                      ST16550_UART_IIR_TRIGGER_LEVEL_14;
      uart_ptr->IIR = (uchar)io_info_ptr->FIFO_CONTROL_REG;
      _BSP_IO_EIEIO
      io_info_ptr->FIFO_CONTROL_REG &= ~(ST16550_UART_IIR_RCVR_FIFO_RESET | ST16550_UART_IIR_XMIT_FIFO_RESET);
      uart_ptr->IIR = (uchar)io_info_ptr->FIFO_CONTROL_REG;
      
      _BSP_IO_EIEIO;

   } /* Endif */
   
   /* set baud rate mode */
   tmp = (uint_32)uart_ptr->LCR;
   _BSP_IO_EIEIO;
   uart_ptr->LCR = tmp | ST16550_UART_LCR_DLAB;
   _BSP_IO_EIEIO;

   div = io_init_ptr->CLOCK_SPEED / ( io_init_ptr->BAUD_RATE * 16 );
   /* LSB of baud rate */
   uart_ptr->DATA = (uchar)(div & 0xFF);
   _BSP_IO_EIEIO;
   /* MSB of baud rate */
   uart_ptr->IER = (uchar)((div >> 8) & 0xFF);
   _BSP_IO_EIEIO;
 
   /* Back to normal operation mode */
   uart_ptr->LCR = (uchar)tmp;
   _BSP_IO_EIEIO;
   
   /* not setting baud rate any more */
   tmp = 0;
   switch (io_init_ptr->BITS_PER_CHARACTER) {
      case 5:
         tmp |= ST16550_UART_LCR_WORD_LEN5;
         break;
      case 6:
         tmp |= ST16550_UART_LCR_WORD_LEN6;
         break;
      case 7:
         tmp |= ST16550_UART_LCR_WORD_LEN7;
         break;
      default:
      case 8:
         tmp |= ST16550_UART_LCR_WORD_LEN8;
         break;
   } /* Endswitch */

   switch (io_init_ptr->PARITY) {
     case IO_SERIAL_EVEN_PARITY:
        tmp |= ST16550_UART_LCR_PARITY_EVEN;
        break;
     case IO_SERIAL_ODD_PARITY:
        tmp |= ST16550_UART_LCR_PARITY_ODD;
        break;
     default:
     case IO_SERIAL_NO_PARITY:
        tmp |= ST16550_UART_LCR_PARITY_NONE;
        break;
   } /* Endswitch */

   switch (io_init_ptr->STOP_BITS) {
      case IO_SERIAL_1_5_STOP_BITS:
      case IO_SERIAL_2_STOP_BITS:
         tmp |= ST16550_UART_LCR_2_STOP_BIT;
         break;
      default:
      case IO_SERIAL_1_STOP_BIT:
         tmp |= ST16550_UART_LCR_1_STOP_BIT;
         break;
   } /* Endswitch */

   io_info_ptr->CURRENT_CONFIGURATION = tmp;
   uart_ptr->LCR = tmp;
   _BSP_IO_EIEIO;
   
   tmp = uart_ptr->MSR;
   _BSP_IO_EIEIO;

   tmp = uart_ptr->LSR;
   _BSP_IO_EIEIO;

   io_info_ptr->CONTROL_BITS = ST16550_UART_MCR_DTR | ST16550_UART_MCR_RTS;
   uart_ptr->MCR = io_info_ptr->CONTROL_BITS;
   _BSP_IO_EIEIO;
   

    /* flush pending input characters */
   timeout = 0xff;
   while ( (uart_ptr->LSR & ST16550_UART_LSR_DATA_RCVD) && --timeout ) { 
      _BSP_IO_EIEIO;
      tmp = uart_ptr->DATA;
      _BSP_IO_EIEIO;
   } /* Endwhile */
   _BSP_IO_EIEIO;
   
   if (io_info_ptr->INT_MODE == IO_SERIAL_INT_MODE_INTERRUPT) {     
      /* Enable Serial Interrupts */
      uart_ptr->IER = ST16550_UART_IER_DATA_AVAIL 
                      | ST16550_UART_IER_XMIT_EMPTY
                      | ST16550_UART_IER_LINE_STATUS
                      | ST16550_UART_IER_MDM_STATUS;
   }

   _BSP_IO_ISYNC;   

   return MQX_OK;

} /* Endbody */

/*FUNCTION****************************************************************
* 
* Function Name    : _st16550_serial_polled_init  
* Returned Value   : MQX_OK or a MQX error code.
* Comments         :
*    This function initializes the uart
*
*END*********************************************************************/

uint_32 _st16550_serial_polled_init
   (
      /* [IN] the initialization information for the device being opened */
      ST16550_SERIAL_INIT_STRUCT_PTR io_init_ptr,

      /* [OUT] the address to store device specific information */
      pointer _PTR_                  io_info_ptr_ptr,
       
      /* [IN] the rest of the name of the device opened */
      char_ptr                       open_name_ptr
   )                       
{ /* Body */
   ST16550_SERIAL_INFO_STRUCT_PTR      io_info_ptr;
   volatile ST16550_UART_STRUCT _PTR_  uart_ptr;

   io_info_ptr  = (ST16550_SERIAL_INFO_STRUCT_PTR)_mem_alloc_system_zero(
      (_mem_size)sizeof(ST16550_SERIAL_INFO_STRUCT));
#if MQX_CHECK_MEMORY_ALLOCATION_ERRORS
   if (!io_info_ptr) {
      return(MQX_OUT_OF_MEMORY);
   } /* Endif */
#endif
   _mem_set_type(io_info_ptr,MEM_TYPE_IO_SERIAL_INFO_STRUCT);    

   uart_ptr = (volatile ST16550_UART_STRUCT _PTR_)io_init_ptr->COM_PORT_ADDRESS;

   *io_info_ptr_ptr      = io_info_ptr;

   io_info_ptr->INIT     = *io_init_ptr;
   io_info_ptr->UART_PTR = uart_ptr;

   return reconfigure(io_info_ptr);

} /* Endbody */


/*FUNCTION****************************************************************
* 
* Function Name    : _st16550_serial_polled_deinit  
* Returned Value   : MQX_OK or a mqx error code.
* Comments         :
*    This function de-initializes the UART
*
*END*********************************************************************/

_mqx_uint _st16550_serial_polled_deinit
   (
      /* [IN] the initialization information for the device being opened */
      ST16550_SERIAL_INIT_STRUCT_PTR io_init_ptr,

      /* [IN] the address of the device specific information */
      ST16550_SERIAL_INFO_STRUCT_PTR io_info_ptr
   )
{ /* Body */
   volatile ST16550_UART_STRUCT _PTR_   uart_ptr;

   uart_ptr = io_info_ptr->UART_PTR;

   uart_ptr->MCR = 0; /* tell chip to NOT interrupt */   
   _BSP_IO_EIEIO;
   uart_ptr->IER = 0; /* Reset interrupts */
   _BSP_IO_EIEIO;
   uart_ptr->IIR = 0; /* Reset FIFOs to char mode */
   _BSP_IO_EIEIO;

   _mem_free((pointer)io_info_ptr);

   return MQX_OK;

} /* Endbody */



/*FUNCTION****************************************************************
* 
* Function Name    : _st16550_serial_getc
* Returned Value   : char
* Comments         : 
*   Return a character when it is available.  This function polls the 
* device for input.
*
*************************************************************************/

char _st16550_serial_getc
   (
      /* [IN] the address of the device specific information */
      ST16550_SERIAL_INFO_STRUCT_PTR io_info_ptr
   )
{ /* Body */
   volatile ST16550_UART_STRUCT _PTR_   uart_ptr;
   uchar                                data;

   uart_ptr = io_info_ptr->UART_PTR;

   data = uart_ptr->LSR;
   _BSP_IO_EIEIO;
   while (!(data & ST16550_UART_LSR_DATA_RCVD)) {
      data = uart_ptr->LSR;
      _BSP_IO_EIEIO;
   } /* Endwhile */
   data = uart_ptr->DATA;

   io_info_ptr->RX_CHARS++;

   return (char)data;

} /* Endbody */


/*FUNCTION****************************************************************
* 
* Function Name    : _st16550_serial_putc
* Returned Value   : void
* Comments         : 
*   Writes the provided character.
*
*END*********************************************************************/

void _st16550_serial_putc
   (
      /* [IN] the address of the device specific information */
      ST16550_SERIAL_INFO_STRUCT_PTR io_info_ptr,

      /* [IN] the character to write */
      char                           c
   )
{ /* Body */
   volatile ST16550_UART_STRUCT _PTR_   uart_ptr;
   uchar                                level;

   uart_ptr = io_info_ptr->UART_PTR;

   level = uart_ptr->LSR;
   _BSP_IO_EIEIO;
   while (!(level & ST16550_UART_LSR_TXD_EMPTY)) {
      level = uart_ptr->LSR;
      _BSP_IO_EIEIO;
   } /* Endwhile */
   uart_ptr->DATA = (uchar)c;

   io_info_ptr->TX_CHARS++;

} /* Endbody */


/*FUNCTION****************************************************************
* 
* Function Name    : _st16550_serial_status
* Returned Value   : boolean
* Comments         : 
*            This function returns TRUE if a character is available
*            on the on I/O device, otherwise it returns FALSE.
*
*END*********************************************************************/

boolean _st16550_serial_status
   (
      /* [IN] the address of the device specific information */
      ST16550_SERIAL_INFO_STRUCT_PTR io_info_ptr
   )
{ /* Body */
   volatile ST16550_UART_STRUCT _PTR_   uart_ptr;

   uart_ptr = io_info_ptr->UART_PTR;

   if (uart_ptr->LSR & ST16550_UART_LSR_DATA_RCVD) {
      return TRUE;
   } else {
      return FALSE;
   } /* Endif */

} /* Endbody */


/*FUNCTION****************************************************************
* 
* Function Name    : _st16550_serial_ioctl
* Returned Value   : uint_32 MQX_OK or a mqx error code.
* Comments         : 
*    This function performs miscellaneous services for
*    the I/O device.  
*
*END*********************************************************************/

uint_32 _st16550_serial_ioctl
   (
      /* [IN] the address of the device specific information */
      ST16550_SERIAL_INFO_STRUCT_PTR io_info_ptr,

      /* [IN] The command to perform */
      _mqx_uint                      cmd,

      /* [IN] Parameters for the command */
      pointer                        param_ptr
   )
{ /* Body */
   ST16550_SERIAL_INIT_STRUCT_PTR       io_init_ptr;
   volatile ST16550_UART_STRUCT _PTR_   uart_ptr;
   uint_32_ptr                          u32_ptr = (uint_32_ptr)param_ptr;
   uint_32                              tmp;
 
   /* Moved from case IO_IOCTL_SERIAL_GET_BAUD file SPR if this works! */
   uart_ptr = io_info_ptr->UART_PTR;
 
   switch (cmd) {
      case IO_IOCTL_SERIAL_GET_STOP_BITS:
         *u32_ptr = io_info_ptr->INIT.STOP_BITS;
         break;
      case IO_IOCTL_SERIAL_SET_STOP_BITS:
         io_info_ptr->INIT.STOP_BITS = *u32_ptr;
         return reconfigure(io_info_ptr);

      case IO_IOCTL_SERIAL_GET_DATA_BITS:
         *u32_ptr = io_info_ptr->INIT.BITS_PER_CHARACTER;
         break;
      case IO_IOCTL_SERIAL_SET_DATA_BITS:
         io_info_ptr->INIT.BITS_PER_CHARACTER = *u32_ptr;
         return reconfigure(io_info_ptr);

      case IO_IOCTL_SERIAL_GET_BAUD:
         *u32_ptr = io_info_ptr->INIT.BAUD_RATE;
         break;
      case IO_IOCTL_SERIAL_SET_BAUD:
         io_info_ptr->INIT.BAUD_RATE = *u32_ptr;
         return reconfigure(io_info_ptr);

      case IO_IOCTL_SERIAL_GET_STATS:
         *u32_ptr++ = io_info_ptr->RX_DROPPED_INPUT;
         *u32_ptr++ = io_info_ptr->INTERRUPTS;
         *u32_ptr++ = io_info_ptr->RX_GOOD_CHARS;
         *u32_ptr++ = io_info_ptr->RX_CHARS;
         *u32_ptr++ = io_info_ptr->TX_CHARS;
         *u32_ptr++ = io_info_ptr->RX_PARITY_ERRORS;
         *u32_ptr++ = io_info_ptr->RX_FRAMING_ERRORS;
         *u32_ptr++ = io_info_ptr->RX_OVERRUNS;
         *u32_ptr++ = io_info_ptr->RX_BREAKS;
         break;

      case IO_IOCTL_SERIAL_CLEAR_STATS:
         io_info_ptr->RX_DROPPED_INPUT  = 0;
         io_info_ptr->INTERRUPTS        = 0;
         io_info_ptr->RX_GOOD_CHARS     = 0;
         io_info_ptr->RX_CHARS          = 0;
         io_info_ptr->TX_CHARS          = 0;
         io_info_ptr->RX_PARITY_ERRORS  = 0;
         io_info_ptr->RX_FRAMING_ERRORS = 0;
         io_info_ptr->RX_OVERRUNS       = 0;
         io_info_ptr->RX_BREAKS         = 0;
         break;

      case IO_IOCTL_SERIAL_GET_CONFIG:
         io_init_ptr = (ST16550_SERIAL_INIT_STRUCT_PTR)param_ptr;
         *io_init_ptr = io_info_ptr->INIT;
         break;
         
      case IO_IOCTL_SERIAL_GET_HW_SIGNAL:
         tmp = 0;
         if (uart_ptr->MSR & ST16550_UART_MSR_CTS) {
            tmp |= IO_SERIAL_CTS;
         }/* Endif */
         if (uart_ptr->MSR & ST16550_UART_MSR_DSR) {
            tmp |= IO_SERIAL_DSR;
         }/* Endif */
         if (io_info_ptr->CONTROL_BITS & ST16550_UART_MCR_RTS) {
            tmp |= IO_SERIAL_RTS;
         }/* Endif */
         if (io_info_ptr->CONTROL_BITS & ST16550_UART_MCR_DTR) {
            tmp |= IO_SERIAL_DTR;
         }/* Endif */
         *u32_ptr = tmp;
         break;

      case IO_IOCTL_SERIAL_SET_HW_SIGNAL:  /* Assert */
         if (*u32_ptr & IO_SERIAL_RTS) {
            io_info_ptr->CONTROL_BITS |= ST16550_UART_MCR_RTS;
         }/* Endif */
         if (*u32_ptr & IO_SERIAL_DTR) {
            io_info_ptr->CONTROL_BITS |= ST16550_UART_MCR_DTR;
         }/* Endif */
         if (*u32_ptr & IO_SERIAL_BRK) {
            uart_ptr->LCR |= ST16550_UART_LCR_DLAB;
            break;
         }/* Endif */
         uart_ptr->MCR = io_info_ptr->CONTROL_BITS;
         break;

      case IO_IOCTL_SERIAL_CLEAR_HW_SIGNAL:  /* Negate */
         if (*u32_ptr & IO_SERIAL_RTS) {
            io_info_ptr->CONTROL_BITS &= ~ST16550_UART_MCR_RTS;
         }/* Endif */
         if (*u32_ptr & IO_SERIAL_DTR) {
            io_info_ptr->CONTROL_BITS &= ~ST16550_UART_MCR_DTR;
         }/* Endif */
         if (*u32_ptr & IO_SERIAL_BRK) {
            uart_ptr->LCR &= ~ST16550_UART_LCR_DLAB;
            break;
         }/* Endif */
         uart_ptr->MCR = io_info_ptr->CONTROL_BITS;
         break;
         
      case IO_IOCTL_SERIAL_TRANSMIT_DONE:
         *((boolean _PTR_)param_ptr) = ((uart_ptr->LSR & ST16550_UART_LSR_TXD_EMPTY) == ST16550_UART_LSR_TXD_EMPTY);
         break;
         
      case IO_IOCTL_SERIAL_SET_RTL:  /* Receive Triger Level */
         io_info_ptr->FIFO_CONTROL_REG &= ~ST16550_UART_IIR_RCV_TRIGGER_MASK;
         io_info_ptr->FIFO_CONTROL_REG &= *((uchar _PTR_)param_ptr);
         uart_ptr->MCR = io_info_ptr->FIFO_CONTROL_REG;
         break;
         
      case IO_IOCTL_SERIAL_GET_RTL:  /* Receive Triger Level */
         *((uchar _PTR_)param_ptr) = (uchar)io_info_ptr->FIFO_CONTROL_REG & ST16550_UART_IIR_RCV_TRIGGER_MASK;
         break;
         
      case IO_IOCTL_SERIAL_GET_PARITY:
         *u32_ptr = io_info_ptr->INIT.PARITY;
         break;
         
      case IO_IOCTL_SERIAL_SET_PARITY:
         io_info_ptr->INIT.PARITY = *u32_ptr;
         return reconfigure(io_info_ptr);
                  
      default:
         return IO_ERROR_INVALID_IOCTL_CMD;

   } /* Endswitch */
   
   _BSP_IO_ISYNC;  /* Make sure operation completes before next one */

   return MQX_OK;

} /* Endbody */

/* EOF */
